package com.tools.web.interceptor.limit;

import com.tools.common.object.Note;
import com.tools.web.WebKit;
import com.tools.web.interceptor.auth.AuthenticationManager;
import com.tools.web.work.ResultCode;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

/**
 * IP-URI 请求限流拦截器抽象实现（同一个 IP 访问同一个接口的限流）
 * */
@Note("IP-URI 请求限流拦截器的抽象实现（同一个 IP 访问同一个接口的限流）")
public abstract class AbsRequestLimitInterceptor implements RequestLimitInterceptor {

    @Note("缓存 key 的前缀")
    private final String saveKeyPrefix;

    @Note("请求被限流后的错误信息")
    private final String limitErrorMessage;

    @Note("用户身份认证拦截器")
    private final AuthenticationManager authenticationManager;


    /* **************************************************************************************
     *
     *          构造器
     *
     * **************************************************************************************
     * */

    public AbsRequestLimitInterceptor(RequestLimitSource source) {
        this(source, null);
    }

    public AbsRequestLimitInterceptor(RequestLimitSource source, AuthenticationManager authenticationManager) {
        if(source == null) {
            throw new NullPointerException("RequestLimitSource 不能为 null");
        }
        this.saveKeyPrefix = source.getSaveKeyPrefix();
        this.limitErrorMessage = source.getLimitErrorMessage();
        this.authenticationManager = authenticationManager;
    }

    /* **************************************************************************************
     *
     *          核心逻辑
     *
     * **************************************************************************************
     * */

    @Override
    public final boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if(handler instanceof ResourceHttpRequestHandler) return true;
        HandlerMethod hm = (HandlerMethod) handler;
        Method method = hm.getMethod();
        RequestLimit limit = method.getAnnotation(RequestLimit.class);
        //方法上的优先
        if(limit != null) {
            return this.protected_checkIsAllow(limit, request, response, this.authenticationManager);
        }
        //否则就使用类的
        Class<?> typeClass = method.getDeclaringClass();
        RequestLimit classLimit = typeClass.getAnnotation(RequestLimit.class);
        return classLimit == null || this.protected_checkIsAllow(classLimit, request, response, this.authenticationManager);
    }

    /* **************************************************************************************
     *
     *          提供给子类 / 需要子类实现的功能的方法
     *
     * **************************************************************************************
     * */

    @Note("需要自行实现的检查逻辑")
    protected abstract boolean protected_checkIsAllow(RequestLimit limit, HttpServletRequest request,
                                                      HttpServletResponse response,
                                                      AuthenticationManager authenticationManager) throws Exception;

    public final String getSaveKeyPrefix() {
        return saveKeyPrefix;
    }

    public final String getLimitErrorMessage() {
        return limitErrorMessage;
    }

    public final AuthenticationManager getAuthenticationManager() {
        return authenticationManager;
    }

    @Note("获取缓存 key")
    protected final String protected_getKey(HttpServletRequest request) {
        String clientIP = (String) request.getAttribute(WebKit.CLIENT_IP_NAME);
        return this.saveKeyPrefix + clientIP + ':' + request.getRequestURI();
    }

    @Note("获取请求被限流后的错误信息。注解的优先级最高")
    protected final String protected_getLimitErrorMessage(RequestLimit limit) {
        int code = limit.code();
        String msg = limit.msg();
        if(code < 0 || msg.isEmpty()) {
            return this.limitErrorMessage;
        }
        return "{\"code\":" + code + ", \"message\":\"" + msg + "\"}";
    }


}
